#pragma once

#include <cstdio>
#include <fstream>
#include <string>

#include "lf_queue.hpp"
#include "macros.h"
#include "thread_utils.hpp"
#include "time_utils.h"

namespace Common {
constexpr size_t LOG_QUEUE_SIZE = 8 * 1024 * 1024;

enum class LogType : int8_t {
  CHAR = 0,
  INTEGER = 1,
  LONG_INTEGER = 2,
  LONG_LONG_INTEGER = 3,
  UNSIGNED_INTEGER = 4,
  UNSIGNED_LONG_INTEGER = 5,
  UNSIGNED_LONG_LONG_INTEGER = 6,
  FLOAT = 7,
  DOUBLE = 8
};

struct LogElement {
  LogType type_ = LogType::CHAR;
  union {
    char c;
    int i;
    long l;
    long long ll;
    unsigned u;
    unsigned long ul;
    unsigned long long ull;
    float f;
    double d;
  } u_;
};

/**
 * @brief 异步文件日志器。
 *
 *  - 外部线程通过 pushValue()/log() 将日志写入无锁队列。
 *  - 后台专用线程循环消费队列并刷新到文件，降低主线程 I/O 阻塞。
 *
 * ⚠️ 使用须知：
 *  - Logger 会在构造时创建并启动后台线程，析构时确保队列清空后再退出。
 *  - 队列容量固定，若生产速度过快可能暂时阻塞生产者；需结合业务场景评估缓冲区大小。
 *  - 后台线程由 createAndStartThread 创建，调用方不可复制/移动 Logger 实例。
 */
class Logger final {
public:
  /**
   * @brief 后台线程入口：持续从队列读取日志元素并写入文件。
   *
   * @note
   *  - 当 running_ 由外部置为 false 时循环结束。
   *  - 每轮消费完数据后主动 flush，确保数据及时落盘。
   */
  auto flushQueue() noexcept {
    while (running_) {
      for (auto next = queue_.getNextToRead(); queue_.size() && next;
           next = queue_.getNextToRead()) {
        switch (next->type_) {
        case LogType::CHAR:
          file_ << next->u_.c;
          break;
        case LogType::INTEGER:
          file_ << next->u_.i;
          break;
        case LogType::LONG_INTEGER:
          file_ << next->u_.l;
          break;
        case LogType::LONG_LONG_INTEGER:
          file_ << next->u_.ll;
          break;
        case LogType::UNSIGNED_INTEGER:
          file_ << next->u_.u;
          break;
        case LogType::UNSIGNED_LONG_INTEGER:
          file_ << next->u_.ul;
          break;
        case LogType::UNSIGNED_LONG_LONG_INTEGER:
          file_ << next->u_.ull;
          break;
        case LogType::FLOAT:
          file_ << next->u_.f;
          break;
        case LogType::DOUBLE:
          file_ << next->u_.d;
          break;
        }
        queue_.updateReadIndex();
      }
      file_.flush();

      using namespace std::literals::chrono_literals;
      std::this_thread::sleep_for(10ms);
    }
  }

  /**
   * @brief 构造 Logger 并立即启动后台线程。
   *
   * @param file_name 日志文件名。
   *
   * @throws std::runtime_error（通过 ASSERT）若文件打开失败或线程创建失败。
   */
  explicit Logger(const std::string &file_name)
      : file_name_(file_name), queue_(LOG_QUEUE_SIZE) {
    file_.open(file_name);
    ASSERT(file_.is_open(), "Could not open log file:" + file_name);
    logger_thread_ = createAndStartThread(-1, "Common/Logger " + file_name_,
                                          [this]() { flushQueue(); });
    ASSERT(logger_thread_ != nullptr, "Failed to start Logger thread.");
  }

  /**
   * @brief 析构时先等待队列清空，再停止后台线程并关闭文件。
   *
   * @note
   *  - 通过轮询等待队列 size() 为 0，避免丢失尚未写出的日志。
   *  - join() 保证线程安全退出。
   */
  ~Logger() {
    std::string time_str;
    std::cerr << Common::getCurrentTimeStr(&time_str)
              << " Flushing and closing Logger for " << file_name_ << std::endl;

    while (queue_.size()) {
      using namespace std::literals::chrono_literals;
      std::this_thread::sleep_for(1s);
    }
    running_ = false;
    logger_thread_->join();

    file_.close();
    std::cerr << Common::getCurrentTimeStr(&time_str) << " Logger for "
              << file_name_ << " exiting." << std::endl;
  }

  /**
   * @brief 将单个日志元素写入环形队列。
   *
  * @param log_element 预先封装好的日志数据。
   */
  auto pushValue(const LogElement &log_element) noexcept {
    *(queue_.getNextToWriteTo()) = log_element;
    queue_.updateWriteIndex();
  }

  /** @name 原生类型写入接口 */
  ///@{
  auto pushValue(const char value) noexcept {
    pushValue(LogElement{LogType::CHAR, {.c = value}});
  }

  auto pushValue(const int value) noexcept {
    pushValue(LogElement{LogType::INTEGER, {.i = value}});
  }

  auto pushValue(const long value) noexcept {
    pushValue(LogElement{LogType::LONG_INTEGER, {.l = value}});
  }

  auto pushValue(const long long value) noexcept {
    pushValue(LogElement{LogType::LONG_LONG_INTEGER, {.ll = value}});
  }

  auto pushValue(const unsigned value) noexcept {
    pushValue(LogElement{LogType::UNSIGNED_INTEGER, {.u = value}});
  }

  auto pushValue(const unsigned long value) noexcept {
    pushValue(LogElement{LogType::UNSIGNED_LONG_INTEGER, {.ul = value}});
  }

  auto pushValue(const unsigned long long value) noexcept {
    pushValue(LogElement{LogType::UNSIGNED_LONG_LONG_INTEGER, {.ull = value}});
  }

  auto pushValue(const float value) noexcept {
    pushValue(LogElement{LogType::FLOAT, {.f = value}});
  }

  auto pushValue(const double value) noexcept {
    pushValue(LogElement{LogType::DOUBLE, {.d = value}});
  }
  ///@}

  /**
   * @brief 写入字符串（C 风格）。
   *
   * @param value 以 '\0' 结尾的 C 字符串，逐字符写入队列。
   */
  auto pushValue(const char *value) noexcept {
    while (*value) {
      pushValue(*value);
      ++value;
    }
  }

  /**
   * @brief 写入 std::string。
   *
   * @param value 字符串对象，内部复用 C 字符串接口。
   */
  auto pushValue(const std::string &value) noexcept {
    pushValue(value.c_str());
  }

  /**
   * @brief 简易格式化输出：顺序替换字符串中的占位符。
   *
   * @tparam T 第一个参数类型。
   * @tparam A 剩余参数类型列表。
   * @param s   格式字符串，使用 % 占位，%% 表示转义的百分号。
   * @param value 第一个替换值。
   * @param args  剩余替换值。
   *
   * @note 若占位符与参数个数不匹配，将调用 FATAL 终止程序。
   */
  template <typename T, typename... A>
  auto log(const char *s, const T &value, A... args) noexcept {
    while (*s) {
      if (*s == '%') {
        if (UNLIKELY(*(s + 1) == '%')) {
          ++s;
        } else {
          pushValue(value);
          log(s + 1, args...);
          return;
        }
      }
      pushValue(*s++);
    }
    FATAL("extra arguments provided to log()");
  }

  /**
   * @brief 无参数版本，确保格式串中不存在未替换的占位符。
   *
   * @param s 格式字符串。
   */
  auto log(const char *s) noexcept {
    while (*s) {
      if (*s == '%') {
        if (UNLIKELY(*(s + 1) == '%')) {
          ++s;
        } else {
          FATAL("missing arguments to log()");
        }
      }
      pushValue(*s++);
    }
  }

  // 禁止默认构造以及拷贝/移动语义，避免多实例共享资源。
  Logger() = delete;
  Logger(const Logger &) = delete;
  Logger(const Logger &&) = delete;
  Logger &operator=(const Logger &) = delete;
  Logger &operator=(const Logger &&) = delete;

private:
  const std::string file_name_;       ///< 日志文件名。
  std::ofstream file_;                ///< 输出文件流句柄。
  LFQueue<LogElement> queue_;         ///< 日志缓冲队列。
  std::atomic<bool> running_ = {true};///< 后台线程运行标志。
  std::thread *logger_thread_ = nullptr; ///< 后台写线程指针。
};
} // namespace Common